#pragma once

#include <AVUIVisual.h>
#include <AVUIRect3D.h>
#include <AVUIIVisual3DContainer.h>

namespace AVUI {

class Visual3DCollection;
class Camera;
class Point4D;

/// <summary>Renders the <see cref="T:System.Windows.Media.Media3D.Visual3D" /> children within the specified 2D viewport bounds.</summary>
class Viewport3DVisual : public Visual, public IVisual3DContainer
{
public:
    DECLARE_ELEMENT(Viewport3DVisual, Visual);

#pragma region Public Methods

    /// <summary>Initializes a new instance of the <see cref="T:System.Windows.Media.Media3D.Viewport3DVisual" /> class.</summary>
    Viewport3DVisual();

    /// <summary>Returns the top-most visual object of a hit test performed at a specified <see cref="T:System.Windows.Point" />.</summary>
    /// <returns>The hit test result of the visual returned as a <see cref="T:System.Windows.Media.HitTestResult" /> type.</returns>
    /// <param name="point">Point against which to hit test.</param>
    TRefCountedPtr<HitTestResult> HitTest(const Point& point) { return Visual::HitTest(point); }

    /// <summary>Initiate a hit test on the <see cref="T:System.Windows.Media.Media3D.Viewport3DVisual" /> by using the <see cref="T:System.Windows.Media.HitTestFilterCallback" /> and <see cref="T:System.Windows.Media.HitTestResultCallback" /> objects.</summary>
    /// <param name="filterCallback">Value of type HitTestFilterCallbackms-help://AstroNS7/wpf_mref/html/T_System_Windows_Media_HitTestFilterCallback.htm.</param>
    /// <param name="resultCallback">Value of type HitTestResultCallbackms-help://AstroNS7/wpf_mref/html/T_System_Windows_Media_HitTestResultCallback.htm.</param>
    /// <param name="hitTestParameters">Value of type <see cref="T:System.Windows.Media.HitTestParameters" />.</param>
    void HitTest(HitTestFilterCallback* pFilterCallback, HitTestResultCallback* pResultCallback, HitTestParameters* pHitTestParameters) { return Visual::HitTest(pFilterCallback, pResultCallback, pHitTestParameters); }

#pragma endregion Public Methods

#pragma region Public Properties

    /// <summary>Gets or sets the <see cref="T:System.Windows.Media.Effects.BitmapEffect" /> applied to the Viewport3DVisual.</summary>
    /// <returns>BitmapEffect applied to the Viewport3DVisual.</returns>
    TRefCountedPtr<BitmapEffect> get_BitmapEffect() { return get_VisualBitmapEffect(); }
    void set_BitmapEffect(BitmapEffect* pValue) { set_VisualBitmapEffect(pValue); }
    DECLARE_PROPERTY(BitmapEffect, BitmapEffect, false);


    /// <summary>Gets or sets the <see cref="T:System.Windows.Media.Effects.BitmapEffectInput" /> applied to the <see cref="T:System.Windows.Media.Media3D.Viewport3DVisual" />. </summary>
    /// <returns>BitmapEffectInput applied to the Viewport3DVisual.</returns>
    TRefCountedPtr<BitmapEffectInput> get_BitmapEffectInput() { return get_VisualBitmapEffectInput(); }
    void set_BitmapEffectInput(BitmapEffectInput* pValue) { set_VisualBitmapEffectInput(pValue); }
    DECLARE_PROPERTY(BitmapEffectInput, BitmapEffectInput, false);

    /// <summary>Gets or sets the <see cref="T:System.Windows.Media.Media3D.Camera" /> used by the <see cref="T:System.Windows.Media.Media3D.Viewport3DVisual" />. This is a dependency property. </summary>
    /// <returns>Camera used by the <see cref="T:System.Windows.Media.Media3D.Viewport3DVisual" />.</returns>
    TRefCountedPtr<Camera> get_Camera() { return (Camera*)(Object*)GetValue(get_CameraProperty()); }
    void set_Camera(Camera* pValue) { SetValue(get_CameraProperty(), (Object*)pValue); } 
    DECLARE_DEPENDENCY_PROPERTY(Camera);

    /// <summary>Gets a collection of <see cref="T:System.Windows.Media.Media3D.Visual3D" /> objects contained by <see cref="T:System.Windows.Media.Media3D.Viewport3DVisual" />. </summary>
    /// <returns>Collection of the objects contained by the <see cref="T:System.Windows.Media.Media3D.Viewport3DVisual" />.</returns>
    Visual3DCollection* get_Children() { return m_pChildren; }
    DECLARE_READONLY_PROPERTY(Children, Visual3DCollection, false);

    /// <summary>Gets or sets the clipping region of the <see cref="T:System.Windows.Media.Media3D.Viewport3DVisual" />.</summary>
    /// <returns>
    /// <see cref="T:System.Windows.Media.Geometry" /> that defines the clipping area.</returns>
    Geometry* get_Clip() { return get_VisualClip(); }
    void set_Clip(Geometry* pClip) { set_VisualClip(pClip); }
    DECLARE_PROPERTY(Clip, Geometry, false);
 
    /// <summary>Gets the bounding box for the contents of the <see cref="T:System.Windows.Media.Media3D.Viewport3DVisual" />.</summary>
    /// <returns>
    /// <see cref="T:System.Windows.Rect" /> that defines the bounding box.</returns>
    Rect get_ContentBounds() { return get_VisualContentBounds(); }
    DECLARE_READONLY_PROPERTY(ContentBounds, Rect, false);

    /// <summary>Gets the union of all of the content bounding boxes for all of the descendants of the <see cref="T:System.Windows.Media.Media3D.Viewport3DVisual" />, but not including the contents of the Viewport3DVisual.</summary>
    /// <returns>
    /// <see cref="T:System.Windows.Rect" /> that defines the union.</returns>
    Rect get_DescendantBounds() { return get_VisualDescendantBounds(); }
    DECLARE_READONLY_PROPERTY(DescendantBounds, Rect, false);

    /// <summary>Gets or sets the offset value of the <see cref="T:System.Windows.Media.Media3D.Viewport3DVisual" />.</summary>
    /// <returns>
    /// <see cref="T:System.Windows.Vector" /> that represents the offset value of the Viewport3DVisual.</returns>
    Vector get_Offset() { return get_VisualOffset(); }
    void set_Offset(const Vector& offset) { set_VisualOffset(offset); }
    DECLARE_PROPERTY(Offset, Vector, false);

    /// <summary>Gets or sets the opacity of the <see cref="T:System.Windows.Media.Media3D.Viewport3DVisual" />.</summary>
    /// <returns>The value of the Opacity property is expressed as a value between 0 and 1, specifying a range from fully transparent to fully opaque. A value of 0 indicates that the Opacity is completely transparent, while a value of 1 indicates that the Opacity is completely opaque. A value 0.5 would indicate the Opacity is 50% opaque, a value of 0.725 would indicate the Opacity is 72.5% opaque, and so on. Values less than 0 are treated as 0, while values greater than 1 are treated as 1.</returns>
    float get_Opacity() { return get_VisualOpacity(); }
    void set_Opacity(float opacity) { set_VisualOpacity(opacity); }
    DECLARE_PROPERTY(Opacity, float, false);

    /// <summary>Gets or sets the opacity mask value of the <see cref="T:System.Windows.Media.Media3D.Viewport3DVisual" />.</summary>
    /// <returns>
    /// <see cref="T:System.Windows.Media.Brush" /> that represents the opacity mask value of the Viewport3DVisual.</returns>
    Brush* get_OpacityMask() { return get_VisualOpacityMask(); }
    void set_OpacityMask(Brush* pValue) { set_VisualOpacityMask(pValue); }
    DECLARE_PROPERTY(OpacityMask, Brush, false);

    /// <summary>Gets the parent <see cref="T:System.Windows.Media.Visual" /> for the Viewport3DVisual.</summary>
    /// <returns>Visual parent of the Viewport3DVisual.</returns>
    DependencyObject* get_Parent() { return get_VisualParent(); }
    DECLARE_READONLY_PROPERTY(Parent, Visual, false);

    /// <summary>Gets or sets the transform value of the <see cref="T:System.Windows.Media.Media3D.Viewport3DVisual" />.</summary>
    /// <returns>
    /// <see cref="T:System.Windows.Media.Transform" /> applied to the Viewport3DVisual.</returns>
    Transform* get_Transform() { return get_VisualTransform(); }
    void set_Transform(Transform* pTransform) { set_VisualTransform(pTransform); }
    DECLARE_PROPERTY(Transform, Transform, false);


    /// <summary>Gets or sets the rectangle in which the <see cref="T:System.Windows.Media.Media3D.Viewport3DVisual" /> will be rendered. This is a dependency property. </summary>
    /// <returns>Rectangle in which the contents of the Viewport3D will be rendered.</returns>
    Rect get_Viewport() { return AVUI::UnboxValue<Rect>(GetValue(get_ViewportProperty())); }
    void set_Viewport(const Rect& value) { SetValue(get_ViewportProperty(), BoxValue(value)); } 
    DECLARE_DEPENDENCY_PROPERTY(Viewport); 

#pragma endregion Public Properties

protected:

#pragma region Protected Methods

    virtual TRefCountedPtr<GeometryHitTestResult> HitTestCore(GeometryHitTestParameters* pHitTestParameters) AVUI_OVERRIDE { LibraryCriticalError(); return NULL; }

#pragma endregion Protected Methods

public:

#pragma region Internal Methods

    virtual Rect CalculateSubgraphBoundsInnerSpace(bool renderBounds) AVUI_OVERRIDE;
    Rect3D ComputeSubgraphBounds3D();
    virtual Rect GetHitTestBounds() AVUI_OVERRIDE;
    virtual HitTestResultBehavior::Enum HitTestPointInternal(HitTestFilterCallback* pFilterCallback, HitTestResultCallback* pResultCallback, PointHitTestParameters* pHitTestParameters) AVUI_OVERRIDE;
    virtual DependencyObject* InternalGet2DOr3DVisualChild(int index) AVUI_OVERRIDE;
    virtual void PrecomputeContent() AVUI_OVERRIDE;
    void SetInheritanceContextForChildren(DependencyObject* pInheritanceContextForChildren) { m_pInheritanceContextForChildren = pInheritanceContextForChildren; }
    void Visual3DTreeChanged();
    Point WorldToViewport(Point4D point);

#pragma endregion Internal Methods

#pragma region Internal Properties

    virtual int get_InternalVisual2DOr3DChildrenCount() AVUI_OVERRIDE;

#pragma endregion Internal Properties

#pragma region IVisual3DContainer Methods

    virtual void AddChild(Visual3D* pChild) AVUI_OVERRIDE;
    virtual Visual3D* GetChild(int index) AVUI_OVERRIDE;
    virtual int GetChildrenCount() AVUI_OVERRIDE;
    virtual void RemoveChild(Visual3D* pChild) AVUI_OVERRIDE;
    virtual void VerifyAPIReadOnly() AVUI_OVERRIDE;
    virtual void VerifyAPIReadOnly(DependencyObject* pOther) AVUI_OVERRIDE;
    virtual void VerifyAPIReadWrite() AVUI_OVERRIDE;
    virtual void VerifyAPIReadWrite(DependencyObject* pOther) AVUI_OVERRIDE;

#pragma endregion IVisual3DContainer Methods

#pragma region WPFG / Deviation

    void Render3D(Renderer* pRenderer, const Matrix& localMatrix, const Rect& localClipRect);
    void RenderDependencies3D(Renderer* pRenderer, const Matrix& localMatrix, const Rect& localClipRect);

#pragma endregion WPFG / Deviation

private:

#pragma region Private Methods
    static void StaticConstructor();
    static void CameraPropertyChanged(DependencyObject* pDO, DependencyPropertyChangedEventArgs* e);
    static void ViewportPropertyChanged(DependencyObject* pDO, DependencyPropertyChangedEventArgs* e);

#pragma endregion Private Methods

#pragma region Private Properties

    Rect3D get_BBoxSubgraph() { return m_bboxChildrenSubgraph3D; }

#pragma endregion Private Properties

#pragma region Private Fields
    Rect3D m_bboxChildrenSubgraph3D;
    TRefCountedPtr<Visual3DCollection> m_pChildren;
    TWeakPtr<DependencyObject> m_pInheritanceContextForChildren;
#pragma endregion Private Fields

};

}; // namespace AVUI



